package com.frank.oj.manager;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.frank.model.entity.contest.Contest;
import com.frank.model.entity.contest.ContestProblem;
import com.frank.oj.common.exception.StatusFailException;
import com.frank.oj.common.exception.StatusForbiddenException;
import com.frank.oj.common.exception.StatusNotFoundException;
import com.frank.oj.constant.Constants;
import com.frank.oj.model.dto.ContestRankDTO;
import com.frank.oj.model.vo.ContestOutsideInfoVO;
import com.frank.oj.model.vo.ContestVO;
import com.frank.oj.service.entity.contest.ContestEntityService;
import com.frank.oj.service.entity.contest.ContestProblemEntityService;
import com.frank.oj.shiro.AccountProfile;
import com.frank.oj.validator.ContestValidator;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author frank
 * @Date 2024/4/22
 */
@Component
public class ContestScoreBoardManager {
    @Resource
    private ContestEntityService contestEntityService;

    @Resource
    private ContestProblemEntityService contestProblemEntityService;

    @Resource
    private ContestValidator contestValidator;

    @Resource
    private ContestRankManager contestRankManager;

    public ContestOutsideInfoVO getContestOutsideInfo(Long cid) throws StatusNotFoundException, StatusForbiddenException {
        ContestVO contestInfo = contestEntityService.getContestInfoById(cid);

        if (contestInfo == null) {
            throw new StatusNotFoundException("访问错误：该比赛不存在！");
        }

        if (!contestInfo.getOpenRank()) {
            throw new StatusForbiddenException("本场比赛未开启外榜，禁止访问外榜！");
        }

        if (contestInfo.getStatus().equals(Constants.Contest.STATUS_SCHEDULED.getCode())) {
            throw new StatusForbiddenException("本场比赛正在筹备中，敬请期待！");
        }

        contestInfo.setNow(new Date());
        ContestOutsideInfoVO contestOutsideInfoVO = new ContestOutsideInfoVO();
        contestOutsideInfoVO.setContest(contestInfo);

        LambdaQueryWrapper<ContestProblem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContestProblem::getCid, cid).orderByAsc(ContestProblem::getDisplayId);
        List<ContestProblem> contestProblems = contestProblemEntityService.list(queryWrapper);
        contestOutsideInfoVO.setProblemList(contestProblems);

        return contestOutsideInfoVO;
    }

    public IPage getContestOutsideScoreboard(ContestRankDTO contestRankDto) throws StatusFailException, StatusForbiddenException {
        Long cid = contestRankDto.getCid();
        List<String> concernedList = contestRankDto.getConcernedList();
        Boolean removeStar = contestRankDto.getRemoveStar();
        Boolean forceRefresh = contestRankDto.getForceRefresh();
        Integer currentPage = contestRankDto.getCurrentPage();
        Integer limit = contestRankDto.getLimit();

        if (currentPage == null || currentPage < 1) currentPage = 1;
        if (limit == null || limit < 1) limit = 50;

        if (cid == null) {
            throw new StatusFailException("参数错误");
        }
        if (removeStar == null) {
            removeStar = false;
        }
        if (forceRefresh == null) {
            forceRefresh = false;
        }

        Contest contest = contestEntityService.getById(cid);
        if (contest == null) {
            throw new StatusFailException("访问错误：该比赛不存在！");
        }

        if (!contest.getOpenRank()) {
            throw new StatusForbiddenException("本场比赛未开启外榜！");
        }

        if (contest.getStatus().equals(Constants.Contest.STATUS_SCHEDULED.getCode())) {
            throw new StatusForbiddenException("本场比赛正在筹备中，敬请期待！");
        }

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = false;
        String currentUid = null;

        if (userRole!=null){
            currentUid=userRole.getUid();
            isRoot=SecurityUtils.getSubject().hasRole("root");

            if (!isRoot&&!contest.getUid().equals(currentUid)){ //仅管理员和比赛创建者开始实时榜单
                forceRefresh=false;
            }
        }
        boolean isOpenSealRank = contestValidator.isSealRank(currentUid, contest, forceRefresh, isRoot);
        boolean isContainsAfterContestJudge = Objects.equals(contest.getAllowEndSubmit(), true)
                && Objects.equals(contestRankDto.getContainsEnd(), true);

        if (contest.getType().intValue()==Constants.Contest.TYPE_ACM.getCode()){
            return contestRankManager.getACMContestScoreboard(isOpenSealRank,
                    removeStar,
                    contest,
                    null,
                    concernedList,
                    contestRankDto.getExternalCidList(),
                    currentPage,
                    limit,
                    contestRankDto.getKeyword(),
                    !forceRefresh,
                    15L, // 默认15s缓存
                    isContainsAfterContestJudge);
        }else {
            return contestRankManager.getOIContestScoreboard(isOpenSealRank,
                    removeStar,
                    contest,
                    null,
                    concernedList,
                    contestRankDto.getExternalCidList(),
                    currentPage,
                    limit,
                    contestRankDto.getKeyword(),
                    !forceRefresh,
                    15L, // 默认15s缓存
                    isContainsAfterContestJudge);
        }
    }
}
